package dbmanager

import (
	"fmt"

	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/redislib"
	"gitee.com/night-tc/gobige/serverMgr"
	"github.com/redis/go-redis/v9"
)

/*
RedisUtilDBSession.go 文件提供了对 Redis 数据库的操作封装，主要用于管理玩家在 Redis 中的会话数据。
该文件包含了对玩家会话数据的增删改查操作，支持会话数据的过期时间设置和服务器ID的获取。
*/

// dbsessionUtil 用于管理玩家在Redis中的会话数据
type dbsessionUtil struct {
	pid uint64 // 玩家ID
	// getRedisF 获取Redis连接的方法
	getRedisF func() *redislib.RedisHandleModel
	// Key Redis中的主键
	Key string
}

const (
	// dbsion_Prefix Redis key的前缀
	dbsion_Prefix = "dbsession"
	// dbsion_EntityID Redis中存放entityID的字段
	dbsion_EntityID = "entityID"
	// dbsion_SID 所在的db服务器ID
	dbsion_SID = "DBServerID"
)

// NewEntityUtilDBData 创建一个新的dbsessionUtil实例
func NewEntityUtilDBData(pid uint64) (result *dbsessionUtil) {
	result = new(dbsessionUtil)
	result.pid = pid
	result.getRedisF = global.GetRedisAccess(global.RedisName_Entity).GetConn
	result.Key = fmt.Sprintf("%s:%d", dbsion_Prefix, pid)

	return
}

// GetUserEntityID 获取玩家的EntityID
func (this *dbsessionUtil) GetUserEntityID() (uint64, error) {
	rd := this.getRedisF()
	return rd.HGet(rd.Ctx, this.Key, dbsion_EntityID).Uint64()
}

// GetDBServerID 获取玩家上一次登录过的DBServer地址
func (this *dbsessionUtil) GetDBServerID() (uint64, error) {
	c := this.getRedisF()
	return c.HGet(c.Ctx, this.Key, dbsion_SID).Uint64()
}

// DelDBSession 删除玩家的会话数据
func (this *dbsessionUtil) DelDBSession(sid uint64) {
	c := this.getRedisF()
	_ = c.Watch(c.Ctx, func(tx *redis.Tx) error {
		rdsid, _ := tx.HGet(c.Ctx, this.Key, dbsion_SID).Uint64()
		if rdsid == sid {
			tx.Del(c.Ctx, this.Key)
		}
		return nil
	}, this.Key)
}

// Expire 设置会话数据的过期时间
func (this *dbsessionUtil) Expire() {
	c := this.getRedisF()
	c.Expire(c.Ctx, this.Key, global.Expire3Day)
}

// GetDBServer 获取玩家所在的DB服务器ID
func GetDBServer(dbsutil *dbsessionUtil, nttid uint64) (result uint64, reserr error) {
	smgr := global.GetSrvInst().(serverMgr.IExt).GetDirMgr()
	rd := dbsutil.getRedisF()
	/*
		如果分配的服务器ID不存活，就换一个新的
		为了老的失效
		需要把Token也给设置为空
	*/
	reserr = rd.Watch(rd.Ctx, func(tx *redis.Tx) error {
		srvid, _ := tx.HGet(rd.Ctx, dbsutil.Key, dbsion_SID).Uint64()
		if srvid == 0 {
			//没有分配过服务器
			if sinfo, err := smgr.GetServerByType(global.GetCfgInst().GetGroupID(), global.ServerTypeDB); err != nil {
				logger.Error("DBManager.SetUtil: ", err)
				return err
			} else {
				_, err = tx.TxPipelined(rd.Ctx, func(p redis.Pipeliner) error {
					p.Expire(rd.Ctx, dbsutil.Key, global.Expire3Day)
					return p.HMSet(rd.Ctx, dbsutil.Key,
						dbsion_SID, sinfo.GetServerID(),
						dbsion_EntityID, nttid).Err()
				})
				result = sinfo.GetServerID()
				return err
			}
		} else {
			_, err := smgr.GetServerById(global.GetCfgInst().GetGroupID(), global.ServerTypeDB, srvid)
			if err == nil {
				if global.GetCfgInst().GetSrvType() == global.ServerTypeGateway {
					//只有gateway才会走这个逻辑
					tx.HSet(rd.Ctx, dbsutil.Key, dbsion_EntityID, nttid)
				}
				result = srvid
				return nil
			}
			if sinfo, err := smgr.GetServerByType(global.GetCfgInst().GetGroupID(), global.ServerTypeDB); err != nil {
				logger.Error("DBManager.SetUtil: ", err)
				return err
			} else {
				_, err = tx.TxPipelined(rd.Ctx, func(p redis.Pipeliner) error {
					p.Expire(rd.Ctx, dbsutil.Key, global.Expire3Day)
					return p.HMSet(rd.Ctx, dbsutil.Key,
						dbsion_SID, sinfo.GetServerID(),
						dbsion_EntityID, nttid).Err()
				})
				result = sinfo.GetServerID()
				return err
			}
		}
	}, dbsutil.Key)
	return result, reserr
}
